<template>
  <el-tree :data="treeData" node-key="uid" default-expand-all :expand-on-click-node="false">
    <template #default="{ node, data }">
      <span class="customNode" :data-id="data.uid" @click="onClick(data)">
        <span
          class="nodeEdit"
          :contenteditable="!isReadonly"
          :key="getKey()"
          @keydown.stop="onNodeInputKeydown($event, node)"
          @keyup.stop
          @blur="onBlur($event, node)"
          @paste="onPaste($event, node)"
          v-html="node.label"
        />
      </span>
    </template>
  </el-tree>
</template>

<script>
import { mapState } from "pinia"
import {
  nodeRichTextToTextWithWrap,
  textToNodeRichTextWithWrap,
  createUid,
  htmlEscape,
  handleInputPasteText
} from "simple-mind-map/src/utils"

import { useMind } from "@mind/store"

// 大纲树
export default {
  name: "Outline",

  data() {
    return {
      treeData: [],
      defaultProps: {
        label: "label"
      },
      currentData: null,
      notHandleDataChange: false,
      isHandleNodeTreeRenderEnd: false,
      beInsertNodeUid: "",
      insertType: "",
      isInTreArea: false,
      isAfterCreateNewNode: false
    }
  },
  computed: {
    ...mapState(useMind, ["isDark", "isReadonly"])
  },
  created() {
    window.addEventListener("keydown", this.onKeyDown)
    this.$bus.$on("data_change", this.handleDataChange)
    this.$bus.$on("node_tree_render_end", this.handleNodeTreeRenderEnd)
    this.$bus.$on("hide_text_edit", this.handleHideTextEdit)
  },
  mounted() {
    this.$bus.$on("init", () => {
      this.refresh()
    })
  },
  beforeUnmount() {
    window.removeEventListener("keydown", this.onKeyDown)
    this.$bus.$off("data_change", this.handleDataChange)
    this.$bus.$off("node_tree_render_end", this.handleNodeTreeRenderEnd)
    this.$bus.$off("hide_text_edit", this.handleHideTextEdit)
  },
  methods: {
    handleHideTextEdit() {
      if (this.notHandleDataChange) {
        this.notHandleDataChange = false
        this.refresh()
      }
    },

    handleDataChange() {
      // 在大纲里操作节点时不要响应该事件，否则会重新刷新树
      if (this.notHandleDataChange) {
        this.notHandleDataChange = false
        this.isAfterCreateNewNode = false
        return
      }
      if (this.isAfterCreateNewNode) {
        this.isAfterCreateNewNode = false
        return
      }
      this.refresh()
    },

    handleNodeTreeRenderEnd() {
      // 当前存在未完成的节点插入操作
      if (this.insertType) {
        this[this.insertType]()
        this.insertType = ""
        return
      }
      // 插入了新节点后需要做一些操作
      if (this.isHandleNodeTreeRenderEnd) {
        this.isHandleNodeTreeRenderEnd = false
        this.refresh()
        this.$nextTick(() => {
          this.afterCreateNewNode()
        })
      }
    },

    // 刷新树数据
    refresh() {
      const data = this.$bus.mindMap.getData()
      data.root = true // 标记根节点
      const walk = (root) => {
        let text = root.data.richText ? nodeRichTextToTextWithWrap(root.data.text) : root.data.text
        text = htmlEscape(text)
        text = text.replaceAll(/\n/g, "<br>")
        root.textCache = text // 保存一份修改前的数据，用于对比是否修改了
        root.label = text
        root.uid = root.data.uid
        if (root.children && root.children.length > 0) {
          root.children.forEach((item) => {
            walk(item)
          })
        }
      }
      walk(data)
      this.treeData = [data]
    },

    // 插入了新节点之后
    afterCreateNewNode() {
      // 如果是新插入节点，那么需要手动高亮该节点、定位该节点及聚焦
      const id = this.beInsertNodeUid
      if (id && this.$refs.tree) {
        try {
          this.isAfterCreateNewNode = true
          // 高亮树节点
          this.$refs.tree.setCurrentKey(id)
          const node = this.$refs.tree.getNode(id)
          this.onCurrentChange(node.data)
          // 定位该节点
          this.onClick(node.data)
          // 聚焦该树节点的编辑框
          const el = document.querySelector(`.customNode[data-id="${id}"] .nodeEdit`)
          if (el) {
            const selection = window.getSelection()
            const range = document.createRange()
            range.selectNodeContents(el)
            selection.removeAllRanges()
            selection.addRange(range)
            const offsetTop = el.offsetTop
            this.$emit("scrollTo", offsetTop)
          }
        } catch (error) {
          console.log(error)
        }
      }
      this.beInsertNodeUid = ""
    },

    // 根节点不允许拖拽
    checkAllowDrag(node) {
      return !node.data.root
    },

    // 失去焦点更新节点文本
    onBlur(e, node) {
      // 节点数据没有修改
      if (node.data.textCache === e.target.innerHTML) {
        // 如果存在未执行的插入新节点操作，那么直接执行
        if (this.insertType) {
          this[this.insertType]()
          this.insertType = ""
        }
        return
      }
      // 否则插入新节点操作需要等待当前修改事件渲染完成后再执行
      const richText = node.data.data.richText
      const text = richText ? e.target.innerHTML : e.target.innerText
      const targetNode = this.$bus.mindMap.renderer.findNodeByUid(node.data.uid)
      if (!targetNode) return
      this.notHandleDataChange = true
      if (richText) {
        targetNode.setText(textToNodeRichTextWithWrap(text), true, true)
      } else {
        targetNode.setText(text)
      }
    },

    // 拦截粘贴事件
    onPaste(e) {
      handleInputPasteText(e)
    },

    // 生成唯一的key
    getKey() {
      return Math.random()
    },

    // 节点输入区域按键事件
    onNodeInputKeydown(e) {
      if (e.keyCode === 13 && !e.shiftKey) {
        // 插入兄弟节点
        e.preventDefault()
        this.insertType = "insertNode"
        e.target.blur()
      }
      if (e.keyCode === 9) {
        e.preventDefault()
        if (e.shiftKey) {
          // 节点上升一级
          this.insertType = "moveUp"
          e.target.blur()
        } else {
          // 插入子节点
          this.insertType = "insertChildNode"
          e.target.blur()
        }
      }
    },

    // 节点上移一个层级
    moveUp() {
      this.$bus.mindMap.execCommand("MOVE_UP_ONE_LEVEL")
    },

    // 插入兄弟节点
    insertNode() {
      this.notHandleDataChange = true
      this.isHandleNodeTreeRenderEnd = true
      this.beInsertNodeUid = createUid()
      this.$bus.mindMap.execCommand("INSERT_NODE", false, [], {
        uid: this.beInsertNodeUid
      })
    },

    // 插入下级节点
    insertChildNode() {
      this.notHandleDataChange = true
      this.isHandleNodeTreeRenderEnd = true
      this.beInsertNodeUid = createUid()
      this.$bus.mindMap.execCommand("INSERT_CHILD_NODE", false, [], {
        uid: this.beInsertNodeUid
      })
    },

    // 激活当前节点且移动当前节点到画布中间
    onClick(data) {
      this.notHandleDataChange = true
      const targetNode = this.$bus.mindMap.renderer.findNodeByUid(data.uid)
      if (targetNode && targetNode.nodeData.data.isActive) return
      this.$bus.mindMap.execCommand("GO_TARGET_NODE", data.uid, () => {
        this.notHandleDataChange = false
      })
    },

    // 拖拽结束事件
    onNodeDrop(data, target, postion) {
      this.notHandleDataChange = true
      const node = this.$bus.mindMap.renderer.findNodeByUid(data.data.uid)
      const targetNode = this.$bus.mindMap.renderer.findNodeByUid(target.data.uid)
      if (!node || !targetNode) {
        return
      }
      switch (postion) {
        case "before":
          this.$bus.mindMap.execCommand("INSERT_BEFORE", node, targetNode)
          break
        case "after":
          this.$bus.mindMap.execCommand("INSERT_AFTER", node, targetNode)
          break
        case "inner":
          this.$bus.mindMap.execCommand("MOVE_NODE_TO", node, targetNode)
          break
        default:
          break
      }
    },

    // 当前选中的树节点变化事件
    onCurrentChange(data) {
      this.currentData = data
    },

    // 删除节点
    onKeyDown(e) {
      if (!this.isInTreArea) return
      if ([46, 8].includes(e.keyCode) && this.currentData) {
        e.stopPropagation()
        this.$bus.mindMap.renderer.textEdit.hideEditTextBox()
        const node = this.$bus.mindMap.renderer.findNodeByUid(this.currentData.uid)
        if (node && !node.isRoot) {
          this.notHandleDataChange = true
          this.$refs.tree.remove(this.currentData)
          this.$bus.mindMap.execCommand("REMOVE_NODE", [node])
        }
      }
    }
  }
}
</script>

<style lang="less" scoped>
.customNode {
  width: 100%;
  color: rgba(0, 0, 0, 0.85);
  font-weight: bold;

  .nodeEdit {
    outline: none;
    white-space: normal;
    padding-right: 20px;
  }
}

.outlineTree {
  &.isDark {
    background-color: #262a2e;

    .customNode {
      color: #fff;
    }

    &.el-tree--highlight-current {
      /deep/ .el-tree-node.is-current > .el-tree-node__content {
        background-color: hsla(0, 0%, 100%, 0.05) !important;
      }
    }

    /deep/ .el-tree-node__content:hover,
    .el-upload-list__item:hover {
      background-color: hsla(0, 0%, 100%, 0.02) !important;
    }

    /deep/ .el-tree-node__content {
      .el-tree-node__expand-icon {
        color: #fff;

        &.is-leaf {
          &::after {
            background-color: #fff;
          }
        }
      }
    }
  }

  /deep/ .el-tree-node > .el-tree-node__children {
    overflow: inherit;
  }

  /deep/ .el-tree-node__content {
    height: auto;
    margin: 5px 0;

    .el-tree-node__expand-icon {
      color: #262a2e;

      &.is-leaf {
        color: transparent;
        position: relative;

        &::after {
          background-color: #262a2e;
          position: absolute;
          content: "";
          width: 5px;
          height: 5px;
          border-radius: 50%;
          left: 10px;
          top: 50%;
          transform: translateY(-50%);
        }
      }
    }
  }
}
</style>
